home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / var / lib / python-support / python2.6 / debian_bundle / deb822.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-04-20  |  34.5 KB  |  1,027 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4.  
  5. try:
  6.     import apt_pkg
  7.     _have_apt_pkg = True
  8. except ImportError:
  9.     _have_apt_pkg = False
  10.  
  11. import new
  12. import re
  13. import string
  14. import sys
  15. import StringIO
  16. import UserDict
  17.  
  18. class OrderedSet(object):
  19.     """A set-like object that preserves order when iterating over it
  20.  
  21.     We use this to keep track of keys in Deb822Dict, because it's much faster
  22.     to look up if a key is in a set than in a list.
  23.     """
  24.     
  25.     def __init__(self, iterable = []):
  26.         self._OrderedSet__set = set()
  27.         self._OrderedSet__order = []
  28.         for item in iterable:
  29.             self.add(item)
  30.         
  31.  
  32.     
  33.     def add(self, item):
  34.         if item not in self:
  35.             self._OrderedSet__set.add(item)
  36.             self._OrderedSet__order.append(item)
  37.         
  38.  
  39.     
  40.     def remove(self, item):
  41.         self._OrderedSet__set.remove(item)
  42.         self._OrderedSet__order.remove(item)
  43.  
  44.     
  45.     def __iter__(self):
  46.         return iter(self._OrderedSet__order)
  47.  
  48.     
  49.     def __contains__(self, item):
  50.         return item in self._OrderedSet__set
  51.  
  52.     append = add
  53.     
  54.     def extend(self, iterable):
  55.         for item in iterable:
  56.             self.add(item)
  57.         
  58.  
  59.  
  60.  
  61. class Deb822Dict(object, UserDict.DictMixin):
  62.     '''A dictionary-like object suitable for storing RFC822-like data.
  63.  
  64.     Deb822Dict behaves like a normal dict, except:
  65.         - key lookup is case-insensitive
  66.         - key order is preserved
  67.         - if initialized with a _parsed parameter, it will pull values from
  68.           that dictionary-like object as needed (rather than making a copy).
  69.           The _parsed dict is expected to be able to handle case-insensitive
  70.           keys.
  71.  
  72.     If _parsed is not None, an optional _fields parameter specifies which keys
  73.     in the _parsed dictionary are exposed.
  74.     '''
  75.     
  76.     def __init__(self, _dict = None, _parsed = None, _fields = None):
  77.         self._Deb822Dict__dict = { }
  78.         self._Deb822Dict__keys = OrderedSet()
  79.         self._Deb822Dict__parsed = None
  80.         if _dict is not None:
  81.             if hasattr(_dict, 'items'):
  82.                 items = _dict.items()
  83.             else:
  84.                 items = list(_dict)
  85.             
  86.             try:
  87.                 for k, v in items:
  88.                     self[k] = v
  89.             except ValueError:
  90.                 this = len(self._Deb822Dict__keys)
  91.                 len_ = len(items[this])
  92.                 raise ValueError('dictionary update sequence element #%d has length %d; 2 is required' % (this, len_))
  93.             except:
  94.                 None<EXCEPTION MATCH>ValueError
  95.             
  96.  
  97.         None<EXCEPTION MATCH>ValueError
  98.  
  99.     
  100.     def __setitem__(self, key, value):
  101.         key = _strI(key)
  102.         self._Deb822Dict__keys.add(key)
  103.         self._Deb822Dict__dict[key] = value
  104.  
  105.     
  106.     def __getitem__(self, key):
  107.         key = _strI(key)
  108.         
  109.         try:
  110.             return self._Deb822Dict__dict[key]
  111.         except KeyError:
  112.             if self._Deb822Dict__parsed is not None and key in self._Deb822Dict__keys:
  113.                 return self._Deb822Dict__parsed[key]
  114.             raise 
  115.         except:
  116.             key in self._Deb822Dict__keys
  117.  
  118.  
  119.     
  120.     def __delitem__(self, key):
  121.         key = _strI(key)
  122.         del self._Deb822Dict__dict[key]
  123.         self._Deb822Dict__keys.remove(key)
  124.  
  125.     
  126.     def has_key(self, key):
  127.         key = _strI(key)
  128.         return key in self._Deb822Dict__keys
  129.  
  130.     
  131.     def keys(self):
  132.         return [ str(key) for key in self._Deb822Dict__keys ]
  133.  
  134.     
  135.     def __repr__(self):
  136.         return [] % []([ '%r: %r' % (k, v) for k, v in self.items() ])
  137.  
  138.     
  139.     def __eq__(self, other):
  140.         mykeys = self.keys()
  141.         mykeys.sort()
  142.         otherkeys = other.keys()
  143.         otherkeys.sort()
  144.         if not mykeys == otherkeys:
  145.             return False
  146.         for key in mykeys:
  147.             if self[key] != other[key]:
  148.                 return False
  149.         
  150.         return True
  151.  
  152.     
  153.     def copy(self):
  154.         copy = self.__class__(self)
  155.         return copy
  156.  
  157.  
  158.  
  159. class Deb822(Deb822Dict):
  160.     
  161.     def __init__(self, sequence = None, fields = None, _parsed = None):
  162.         '''Create a new Deb822 instance.
  163.  
  164.         :param sequence: a string, or any any object that returns a line of
  165.             input each time, normally a file().  Alternately, sequence can
  166.             be a dict that contains the initial key-value pairs.
  167.  
  168.         :param fields: if given, it is interpreted as a list of fields that
  169.             should be parsed (the rest will be discarded).
  170.  
  171.         :param _parsed: internal parameter.
  172.         '''
  173.         if hasattr(sequence, 'items'):
  174.             _dict = sequence
  175.             sequence = None
  176.         else:
  177.             _dict = None
  178.         Deb822Dict.__init__(self, _dict = _dict, _parsed = _parsed, _fields = fields)
  179.         if sequence is not None:
  180.             
  181.             try:
  182.                 self._internal_parser(sequence, fields)
  183.             except EOFError:
  184.                 pass
  185.             except:
  186.                 None<EXCEPTION MATCH>EOFError
  187.             
  188.  
  189.         None<EXCEPTION MATCH>EOFError
  190.         self.gpg_info = None
  191.  
  192.     
  193.     def iter_paragraphs(cls, sequence, fields = None, use_apt_pkg = True, shared_storage = False):
  194.         """Generator that yields a Deb822 object for each paragraph in sequence.
  195.  
  196.         :param sequence: same as in __init__.
  197.  
  198.         :param fields: likewise.
  199.  
  200.         :param use_apt_pkg: if sequence is a file(), apt_pkg will be used 
  201.             if available to parse the file, since it's much much faster.  Set
  202.             this parameter to False to disable using apt_pkg.
  203.         :param shared_storage: if sequence is a file(), use_apt_pkg is True,
  204.             and shared_storage is True, yielded objects will share storage, so
  205.             they can't be kept across iterations.  (Also, PGP signatures won't
  206.             be stripped.)  By default, this parameter is False, causing a copy
  207.             of the parsed data to be made through each iteration.  Except for
  208.             with raw Deb822 paragraphs (as opposed to _multivalued subclasses),
  209.             the speed gained by setting shared_storage=True is marginal.  This
  210.             parameter has no effect if use_apt_pkg is False or apt_pkg is not
  211.             available.
  212.         """
  213.         if _have_apt_pkg and use_apt_pkg and isinstance(sequence, file):
  214.             parser = apt_pkg.ParseTagFile(sequence)
  215.             while parser.Step() == 1:
  216.                 yield cls(fields = fields, _parsed = parsed)
  217.                 [] if shared_storage else []
  218.         else:
  219.             iterable = iter(sequence)
  220.             x = cls(iterable, fields)
  221.             while len(x) != 0:
  222.                 yield x
  223.                 x = cls(iterable, fields)
  224.  
  225.     iter_paragraphs = classmethod(iter_paragraphs)
  226.     
  227.     def _internal_parser(self, sequence, fields = None):
  228.         single = re.compile('^(?P<key>\\S+)\\s*:\\s*(?P<data>\\S.*?)\\s*$')
  229.         multi = re.compile('^(?P<key>\\S+)\\s*:\\s*$')
  230.         multidata = re.compile('^\\s(?P<data>.+?)\\s*$')
  231.         
  232.         wanted_field = lambda f: if not fields is None:
  233. passf in fields
  234.         if isinstance(sequence, basestring):
  235.             sequence = sequence.splitlines()
  236.         
  237.         curkey = None
  238.         content = ''
  239.         for line in self.gpg_stripped_paragraph(sequence):
  240.             m = single.match(line)
  241.             if m:
  242.                 if curkey:
  243.                     self[curkey] += content
  244.                 
  245.                 if not wanted_field(m.group('key')):
  246.                     curkey = None
  247.                     continue
  248.                 
  249.                 curkey = m.group('key')
  250.                 self[curkey] = m.group('data')
  251.                 content = ''
  252.                 continue
  253.             
  254.             m = multi.match(line)
  255.             if m:
  256.                 if curkey:
  257.                     self[curkey] += content
  258.                 
  259.                 if not wanted_field(m.group('key')):
  260.                     curkey = None
  261.                     continue
  262.                 
  263.                 curkey = m.group('key')
  264.                 self[curkey] = ''
  265.                 content = ''
  266.                 continue
  267.             
  268.             m = multidata.match(line)
  269.             if m:
  270.                 content += '\n' + line
  271.                 continue
  272.                 continue
  273.         
  274.         if curkey:
  275.             self[curkey] += content
  276.         
  277.  
  278.     
  279.     def __str__(self):
  280.         return self.dump()
  281.  
  282.     
  283.     def dump(self, fd = None):
  284.         '''Dump the the contents in the original format
  285.  
  286.         If fd is None, return a string.
  287.         '''
  288.         if fd is None:
  289.             fd = StringIO.StringIO()
  290.             return_string = True
  291.         else:
  292.             return_string = False
  293.         for key, value in self.iteritems():
  294.             if not value or value[0] == '\n':
  295.                 fd.write('%s:%s\n' % (key, value))
  296.                 continue
  297.             fd.write('%s: %s\n' % (key, value))
  298.         
  299.         if return_string:
  300.             return fd.getvalue()
  301.  
  302.     
  303.     def isSingleLine(self, s):
  304.         if s.count('\n'):
  305.             return False
  306.         return True
  307.  
  308.     
  309.     def isMultiLine(self, s):
  310.         return not self.isSingleLine(s)
  311.  
  312.     
  313.     def _mergeFields(self, s1, s2):
  314.         if not s2:
  315.             return s1
  316.         if not s1:
  317.             return s2
  318.         if self.isSingleLine(s1) and self.isSingleLine(s2):
  319.             delim = ' '
  320.             L = (s1 + delim + s2).split(delim)
  321.             L.sort()
  322.             for item in L[1:]:
  323.                 merged = merged + delim + item
  324.                 prev = item
  325.             
  326.             return merged
  327.         if self.isMultiLine(s1) and self.isMultiLine(s2):
  328.             for item in s2.splitlines(True):
  329.                 if item not in s1.splitlines(True):
  330.                     s1 = s1 + '\n' + item
  331.                     continue
  332.                 self.isSingleLine(s2)
  333.             
  334.             return s1
  335.         raise ValueError
  336.  
  337.     
  338.     def mergeFields(self, key, d1, d2 = None):
  339.         if d2 == None:
  340.             x1 = self
  341.             x2 = d1
  342.         else:
  343.             x1 = d1
  344.             x2 = d2
  345.         if key in x1 and key in x2:
  346.             merged = self._mergeFields(x1[key], x2[key])
  347.         elif key in x1:
  348.             merged = x1[key]
  349.         elif key in x2:
  350.             merged = x2[key]
  351.         else:
  352.             raise KeyError
  353.         if (key in x2) == None:
  354.             self[key] = merged
  355.             return None
  356.         return merged
  357.  
  358.     
  359.     def split_gpg_and_payload(sequence):
  360.         '''Return a (gpg_pre, payload, gpg_post) tuple
  361.         
  362.         Each element of the returned tuple is a list of lines (with trailing
  363.         whitespace stripped).
  364.         '''
  365.         gpg_pre_lines = []
  366.         lines = []
  367.         gpg_post_lines = []
  368.         state = 'SAFE'
  369.         gpgre = re.compile('^-----(?P<action>BEGIN|END) PGP (?P<what>[^-]+)-----$')
  370.         blank_line = re.compile('^$')
  371.         first_line = True
  372.         for line in sequence:
  373.             line = line.strip('\r\n')
  374.             if first_line:
  375.                 if blank_line.match(line):
  376.                     continue
  377.                 else:
  378.                     first_line = False
  379.             
  380.             m = gpgre.match(line)
  381.             if not m:
  382.                 if state == 'SAFE':
  383.                     if not blank_line.match(line):
  384.                         lines.append(line)
  385.                     elif not gpg_pre_lines:
  386.                         break
  387.                     
  388.                 elif state == 'SIGNED MESSAGE':
  389.                     if blank_line.match(line):
  390.                         state = 'SAFE'
  391.                     else:
  392.                         gpg_pre_lines.append(line)
  393.                 elif state == 'SIGNATURE':
  394.                     gpg_post_lines.append(line)
  395.                 
  396.             state == 'SAFE'
  397.             if m.group('action') == 'BEGIN':
  398.                 state = m.group('what')
  399.             elif m.group('action') == 'END':
  400.                 gpg_post_lines.append(line)
  401.                 break
  402.             
  403.             if not blank_line.match(line):
  404.                 if not lines:
  405.                     gpg_pre_lines.append(line)
  406.                 else:
  407.                     gpg_post_lines.append(line)
  408.             lines
  409.         
  410.         if len(lines):
  411.             return (gpg_pre_lines, lines, gpg_post_lines)
  412.         raise EOFError('only blank lines found in input')
  413.  
  414.     split_gpg_and_payload = staticmethod(split_gpg_and_payload)
  415.     
  416.     def gpg_stripped_paragraph(cls, sequence):
  417.         return cls.split_gpg_and_payload(sequence)[1]
  418.  
  419.     gpg_stripped_paragraph = classmethod(gpg_stripped_paragraph)
  420.     
  421.     def get_gpg_info(self):
  422.         '''Return a GpgInfo object with GPG signature information
  423.  
  424.         This method will raise ValueError if the signature is not available
  425.         (e.g. the original text cannot be found)'''
  426.         if not hasattr(self, 'raw_text'):
  427.             raise ValueError, 'original text cannot be found'
  428.         hasattr(self, 'raw_text')
  429.         if self.gpg_info is None:
  430.             self.gpg_info = GpgInfo.from_sequence(self.raw_text)
  431.         
  432.         return self.gpg_info
  433.  
  434.  
  435.  
  436. class GpgInfo(dict):
  437.     '''A wrapper around gnupg parsable output obtained via --status-fd
  438.  
  439.     This class is really a dictionary containing parsed output from gnupg plus
  440.     some methods to make sense of the data.
  441.     Keys are keywords and values are arguments suitably splitted.
  442.     See /usr/share/doc/gnupg/DETAILS.gz'''
  443.     uidkeys = ('GOODSIG', 'EXPSIG', 'EXPKEYSIG', 'REVKEYSIG', 'BADSIG')
  444.     
  445.     def valid(self):
  446.         '''Is the signature valid?'''
  447.         if not self.has_key('GOODSIG'):
  448.             pass
  449.         return self.has_key('VALIDSIG')
  450.  
  451.     
  452.     def uid(self):
  453.         '''Return the primary ID of the signee key, None is not available'''
  454.         pass
  455.  
  456.     
  457.     def from_output(out, err = None):
  458.         '''Create a new GpgInfo object from gpg(v) --status-fd output (out) and
  459.         optionally collect stderr as well (err).
  460.         
  461.         Both out and err can be lines in newline-terminated sequence or regular strings.'''
  462.         n = GpgInfo()
  463.         if isinstance(out, basestring):
  464.             out = out.split('\n')
  465.         
  466.         if isinstance(err, basestring):
  467.             err = err.split('\n')
  468.         
  469.         n.out = out
  470.         n.err = err
  471.         header = '[GNUPG:] '
  472.         for l in out:
  473.             if not l.startswith(header):
  474.                 continue
  475.             
  476.             l = l[len(header):]
  477.             l = l.strip('\n')
  478.             s = l.find(' ')
  479.             key = l[:s]
  480.             if key in GpgInfo.uidkeys:
  481.                 value = l[s + 1:].split(' ', 1)
  482.             else:
  483.                 value = l[s + 1:].split(' ')
  484.             n[key] = value
  485.         
  486.         return n
  487.  
  488.     from_output = staticmethod(from_output)
  489.     
  490.     def from_sequence(sequence, keyrings = [
  491.         '/usr/share/keyrings/debian-keyring.gpg'], executable = [
  492.         '/usr/bin/gpgv']):
  493.         '''Create a new GpgInfo object from the given sequence.
  494.  
  495.         Sequence is a sequence of lines or a string
  496.         executable is a list of args for subprocess.Popen, the first element being the gpg executable'''
  497.         args = executable
  498.         args.extend([
  499.             '--status-fd',
  500.             '1'])
  501.         import os
  502.         _[1]
  503.         if '--keyring' not in args:
  504.             raise IOError, 'cannot access none of given keyrings'
  505.         '--keyring' not in args
  506.         import subprocess
  507.         p = subprocess.Popen(args, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
  508.         return GpgInfo.from_output(out, err)
  509.  
  510.     from_sequence = staticmethod(from_sequence)
  511.     
  512.     def from_file(target, *args):
  513.         '''Create a new GpgInfo object from the given file, calls from_sequence(file(target), *args)'''
  514.         return from_sequence(file(target), *args)
  515.  
  516.     from_file = staticmethod(from_file)
  517.  
  518.  
  519. class PkgRelation(object):
  520.     '''Inter-package relationships
  521.  
  522.     Structured representation of the relationships of a package to another,
  523.     i.e. of what can appear in a Deb882 field like Depends, Recommends,
  524.     Suggests, ... (see Debian Policy 7.1).
  525.     '''
  526.     __dep_RE = re.compile('^\\s*(?P<name>[a-zA-Z0-9.+\\-]{2,})(\\s*\\(\\s*(?P<relop>[>=<]+)\\s*(?P<version>[0-9a-zA-Z:\\-+~.]+)\\s*\\))?(\\s*\\[(?P<archs>[\\s!\\w\\-]+)\\])?\\s*$')
  527.     __comma_sep_RE = re.compile('\\s*,\\s*')
  528.     __pipe_sep_RE = re.compile('\\s*\\|\\s*')
  529.     __blank_sep_RE = re.compile('\\s*')
  530.     
  531.     def parse_relations(cls, raw):
  532.         '''Parse a package relationship string (i.e. the value of a field like
  533.         Depends, Recommends, Build-Depends ...)
  534.         '''
  535.         
  536.         def parse_archs(raw):
  537.             archs = []
  538.             for arch in cls._PkgRelation__blank_sep_RE.split(raw.strip()):
  539.                 if len(arch) and arch[0] == '!':
  540.                     archs.append((False, arch[1:]))
  541.                     continue
  542.                 archs.append((True, arch))
  543.             
  544.             return archs
  545.  
  546.         
  547.         def parse_rel(raw):
  548.             match = cls._PkgRelation__dep_RE.match(raw)
  549.             if match:
  550.                 parts = match.groupdict()
  551.                 d = {
  552.                     'name': parts['name'] }
  553.                 if not parts['relop'] is None or parts['version'] is None:
  554.                     d['version'] = (parts['relop'], parts['version'])
  555.                 else:
  556.                     d['version'] = None
  557.                 if parts['archs'] is None:
  558.                     d['arch'] = None
  559.                 else:
  560.                     d['arch'] = parse_archs(parts['archs'])
  561.                 return d
  562.             print >>sys.stderr, 'deb822.py: WARNING: cannot parse package relationship "%s", returning it raw' % raw
  563.             return {
  564.                 'name': raw,
  565.                 'version': None,
  566.                 'arch': None }
  567.  
  568.         tl_deps = cls._PkgRelation__comma_sep_RE.split(raw.strip())
  569.         cnf = map(cls._PkgRelation__pipe_sep_RE.split, tl_deps)
  570.         return (map,)((lambda or_deps: map(parse_rel, or_deps)), cnf)
  571.  
  572.     parse_relations = classmethod(parse_relations)
  573.     
  574.     def str(rels):
  575.         '''Format to string structured inter-package relationships
  576.         
  577.         Perform the inverse operation of parse_relations, returning a string
  578.         suitable to be written in a package stanza.
  579.         '''
  580.         
  581.         def pp_arch(arch_spec):
  582.             (excl, arch) = arch_spec
  583.             if excl:
  584.                 return arch
  585.             return '!' + arch
  586.  
  587.         
  588.         def pp_atomic_dep(dep):
  589.             s = dep['name']
  590.             if dep.has_key('version') and dep['version'] is not None:
  591.                 s += ' (%s %s)' % dep['version']
  592.             
  593.             if dep.has_key('arch') and dep['arch'] is not None:
  594.                 s += ' [%s]' % string.join(map(pp_arch, dep['arch']))
  595.             
  596.             return s
  597.  
  598.         
  599.         pp_or_dep = lambda deps: string.join(map(pp_atomic_dep, deps), ' | ')
  600.         return string.join(map(pp_or_dep, rels), ', ')
  601.  
  602.     str = staticmethod(str)
  603.  
  604.  
  605. class _lowercase_dict(dict):
  606.     '''Dictionary wrapper which lowercase keys upon lookup.'''
  607.     
  608.     def __getitem__(self, key):
  609.         return dict.__getitem__(self, key.lower())
  610.  
  611.  
  612.  
  613. class _PkgRelationMixin(object):
  614.     """Package relationship mixin
  615.  
  616.     Inheriting from this mixin you can extend a Deb882 object with attributes
  617.     letting you access inter-package relationship in a structured way, rather
  618.     than as strings. For example, while you can usually use pkg['depends'] to
  619.     obtain the Depends string of package pkg, mixing in with this class you
  620.     gain pkg.depends to access Depends as a Pkgrel instance
  621.  
  622.     To use, subclass _PkgRelationMixin from a class with a _relationship_fields
  623.     attribute. It should be a list of field names for which structured access
  624.     is desired; for each of them a method wild be added to the inherited class.
  625.     The method name will be the lowercase version of field name; '-' will be
  626.     mangled as '_'. The method would return relationships in the same format of
  627.     the PkgRelation' relations property.
  628.  
  629.     See Packages and Sources as examples.
  630.     """
  631.     
  632.     def __init__(self, *args, **kwargs):
  633.         self._PkgRelationMixin__relations = _lowercase_dict({ })
  634.         self._PkgRelationMixin__parsed_relations = False
  635.         for name in self._relationship_fields:
  636.             keyname = name.lower()
  637.             if self.has_key(name):
  638.                 self._PkgRelationMixin__relations[keyname] = None
  639.                 continue
  640.             self._PkgRelationMixin__relations[keyname] = []
  641.         
  642.  
  643.     
  644.     def relations(self):
  645.         '''Return a dictionary of inter-package relationships among the current
  646.         and other packages.
  647.  
  648.         Dictionary keys depend on the package kind. Binary packages have keys
  649.         like \'depends\', \'recommends\', ... while source packages have keys like
  650.         \'build-depends\', \'build-depends-indep\' and so on. See the Debian policy
  651.         for the comprehensive field list.
  652.  
  653.         Dictionary values are package relationships returned as lists of lists
  654.         of dictionaries (see below for some examples).
  655.  
  656.         The encoding of package relationships is as follows:
  657.         - the top-level lists corresponds to the comma-separated list of
  658.           Deb822, their components form a conjuction, i.e. they have to be
  659.           AND-ed together
  660.         - the inner lists corresponds to the pipe-separated list of Deb822,
  661.           their components form a disjunction, i.e. they have to be OR-ed
  662.           together
  663.         - member of the inner lists are dictionaries with the following keys:
  664.           - name:       package (or virtual package) name
  665.           - version:    A pair <operator, version> if the relationship is
  666.                         versioned, None otherwise. operator is one of "<<",
  667.                         "<=", "=", ">=", ">>"; version is the given version as
  668.                         a string.
  669.           - arch:       A list of pairs <polarity, architecture> if the
  670.                         relationship is architecture specific, None otherwise.
  671.                         Polarity is a boolean (false if the architecture is
  672.                         negated with "!", true otherwise), architecture the
  673.                         Debian archtiecture name as a string.
  674.  
  675.         Examples:
  676.  
  677.           "emacs | emacsen, make, debianutils (>= 1.7)"     becomes
  678.           [ [ {\'name\': \'emacs\'}, {\'name\': \'emacsen\'} ],
  679.             [ {\'name\': \'make\'} ],
  680.             [ {\'name\': \'debianutils\', \'version\': (\'>=\', \'1.7\')} ] ]
  681.  
  682.           "tcl8.4-dev, procps [!hurd-i386]"                 becomes
  683.           [ [ {\'name\': \'tcl8.4-dev\'} ],
  684.             [ {\'name\': \'procps\', \'arch\': (false, \'hurd-i386\')} ] ]
  685.         '''
  686.         if not self._PkgRelationMixin__parsed_relations:
  687.             lazy_rels = (filter,)((lambda n: self._PkgRelationMixin__relations[n] is None), self._PkgRelationMixin__relations.keys())
  688.             for n in lazy_rels:
  689.                 self._PkgRelationMixin__relations[n] = PkgRelation.parse_relations(self[n])
  690.             
  691.             self._PkgRelationMixin__parsed_relations = True
  692.         
  693.         return self._PkgRelationMixin__relations
  694.  
  695.     relations = property(relations)
  696.  
  697.  
  698. class _multivalued(Deb822):
  699.     '''A class with (R/W) support for multivalued fields.
  700.  
  701.     To use, create a subclass with a _multivalued_fields attribute.  It should
  702.     be a dictionary with *lower-case* keys, with lists of human-readable
  703.     identifiers of the fields as the values.  Please see Dsc, Changes, and
  704.     PdiffIndex as examples.
  705.     '''
  706.     
  707.     def __init__(self, *args, **kwargs):
  708.         Deb822.__init__(self, *args, **kwargs)
  709.         for field, fields in self._multivalued_fields.items():
  710.             
  711.             try:
  712.                 contents = self[field]
  713.             except KeyError:
  714.                 continue
  715.  
  716.             if self.isMultiLine(contents):
  717.                 self[field] = []
  718.                 updater_method = self[field].append
  719.             else:
  720.                 self[field] = Deb822Dict()
  721.                 updater_method = self[field].update
  722.             for line in filter(None, contents.splitlines()):
  723.                 updater_method(Deb822Dict(zip(fields, line.split())))
  724.             
  725.         
  726.  
  727.     
  728.     def dump(self, fd = None):
  729.         '''Dump the contents in the original format
  730.  
  731.         If fd is None, return a string.
  732.         '''
  733.         if fd is None:
  734.             fd = StringIO.StringIO()
  735.             return_string = True
  736.         else:
  737.             return_string = False
  738.         for key in self.keys():
  739.             keyl = key.lower()
  740.             if keyl not in self._multivalued_fields:
  741.                 value = self[key]
  742.                 if not value or value[0] == '\n':
  743.                     fd.write('%s:%s\n' % (key, value))
  744.                 else:
  745.                     fd.write('%s: %s\n' % (key, value))
  746.             value[0] == '\n'
  747.             fd.write(key + ':')
  748.             if hasattr(self[key], 'keys'):
  749.                 array = [
  750.                     self[key]]
  751.             else:
  752.                 fd.write('\n')
  753.                 array = self[key]
  754.             order = self._multivalued_fields[keyl]
  755.             
  756.             try:
  757.                 field_lengths = self._fixed_field_lengths
  758.             except AttributeError:
  759.                 field_lengths = { }
  760.  
  761.             for item in array:
  762.                 for x in order:
  763.                     raw_value = str(item[x])
  764.                     
  765.                     try:
  766.                         length = field_lengths[keyl][x]
  767.                     except KeyError:
  768.                         value = raw_value
  769.  
  770.                     value = (length - len(raw_value)) * ' ' + raw_value
  771.                     fd.write(' %s' % value)
  772.                 
  773.                 fd.write('\n')
  774.             
  775.         
  776.         if return_string:
  777.             return fd.getvalue()
  778.  
  779.  
  780.  
  781. class _gpg_multivalued(_multivalued):
  782.     """A _multivalued class that can support gpg signed objects
  783.  
  784.     This class's feature is that it stores the raw text before parsing so that
  785.     gpg can verify the signature.  Use it just like you would use the
  786.     _multivalued class.
  787.  
  788.     This class only stores raw text if it is given a raw string, or if it
  789.     detects a gpg signature when given a file or sequence of lines (see
  790.     Deb822.split_gpg_and_payload for details).
  791.     """
  792.     
  793.     def __init__(self, *args, **kwargs):
  794.         
  795.         try:
  796.             sequence = args[0]
  797.         except IndexError:
  798.             sequence = kwargs.get('sequence', None)
  799.  
  800.         if sequence is not None:
  801.             if isinstance(sequence, basestring):
  802.                 self.raw_text = sequence
  803.             elif hasattr(sequence, 'items'):
  804.                 pass
  805.             else:
  806.                 
  807.                 try:
  808.                     (gpg_pre_lines, lines, gpg_post_lines) = self.split_gpg_and_payload(sequence)
  809.                 except EOFError:
  810.                     gpg_pre_lines = lines = gpg_post_lines = []
  811.  
  812.                 if gpg_pre_lines and gpg_post_lines:
  813.                     raw_text = StringIO.StringIO()
  814.                     raw_text.write('\n'.join(gpg_pre_lines))
  815.                     raw_text.write('\n\n')
  816.                     raw_text.write('\n'.join(lines))
  817.                     raw_text.write('\n\n')
  818.                     raw_text.write('\n'.join(gpg_post_lines))
  819.                     self.raw_text = raw_text.getvalue()
  820.                 
  821.                 
  822.                 try:
  823.                     args = list(args)
  824.                     args[0] = lines
  825.                 except IndexError:
  826.                     kwargs['sequence'] = lines
  827.                 except:
  828.                     None<EXCEPTION MATCH>IndexError
  829.                 
  830.  
  831.         None<EXCEPTION MATCH>IndexError
  832.         _multivalued.__init__(self, *args, **kwargs)
  833.  
  834.  
  835.  
  836. class Dsc(_gpg_multivalued):
  837.     _multivalued_fields = {
  838.         'files': [
  839.             'md5sum',
  840.             'size',
  841.             'name'],
  842.         'checksums-sha1': [
  843.             'sha1',
  844.             'size',
  845.             'name'],
  846.         'checksums-sha256': [
  847.             'sha256',
  848.             'size',
  849.             'name'] }
  850.  
  851.  
  852. class Changes(_gpg_multivalued):
  853.     _multivalued_fields = {
  854.         'files': [
  855.             'md5sum',
  856.             'size',
  857.             'section',
  858.             'priority',
  859.             'name'],
  860.         'checksums-sha1': [
  861.             'sha1',
  862.             'size',
  863.             'name'],
  864.         'checksums-sha256': [
  865.             'sha256',
  866.             'size',
  867.             'name'] }
  868.     
  869.     def get_pool_path(self):
  870.         '''Return the path in the pool where the files would be installed'''
  871.         s = self['files'][0]['section']
  872.         
  873.         try:
  874.             (section, subsection) = s.split('/')
  875.         except ValueError:
  876.             section = 'main'
  877.  
  878.         if self['source'].startswith('lib'):
  879.             subdir = self['source'][:4]
  880.         else:
  881.             subdir = self['source'][0]
  882.         return 'pool/%s/%s/%s' % (section, subdir, self['source'])
  883.  
  884.  
  885.  
  886. class PdiffIndex(_multivalued):
  887.     _multivalued_fields = {
  888.         'sha1-current': [
  889.             'SHA1',
  890.             'size'],
  891.         'sha1-history': [
  892.             'SHA1',
  893.             'size',
  894.             'date'],
  895.         'sha1-patches': [
  896.             'SHA1',
  897.             'size',
  898.             'date'] }
  899.     
  900.     def _fixed_field_lengths(self):
  901.         fixed_field_lengths = { }
  902.         for key in self._multivalued_fields:
  903.             if hasattr(self[key], 'keys'):
  904.                 continue
  905.             
  906.             length = self._get_size_field_length(key)
  907.             fixed_field_lengths[key] = {
  908.                 'size': length }
  909.         
  910.         return fixed_field_lengths
  911.  
  912.     _fixed_field_lengths = property(_fixed_field_lengths)
  913.     
  914.     def _get_size_field_length(self, key):
  915.         lengths = [ len(str(item['size'])) for item in self[key] ]
  916.         return max(lengths)
  917.  
  918.  
  919.  
  920. class Release(_multivalued):
  921.     '''Represents a Release file
  922.  
  923.     Set the size_field_behavior attribute to "dak" to make the size field
  924.     length only as long as the longest actual value.  The default,
  925.     "apt-ftparchive" makes the field 16 characters long regardless.
  926.     '''
  927.     _multivalued_fields = {
  928.         'md5sum': [
  929.             'md5sum',
  930.             'size',
  931.             'name'],
  932.         'sha1': [
  933.             'sha1',
  934.             'size',
  935.             'name'],
  936.         'sha256': [
  937.             'sha256',
  938.             'size',
  939.             'name'] }
  940.     __size_field_behavior = 'apt-ftparchive'
  941.     
  942.     def set_size_field_behavior(self, value):
  943.         if value not in ('apt-ftparchive', 'dak'):
  944.             raise ValueError("size_field_behavior must be either 'apt-ftparchive' or 'dak'")
  945.         value not in ('apt-ftparchive', 'dak')
  946.         self._Release__size_field_behavior = value
  947.  
  948.     size_field_behavior = property((lambda self: self._Release__size_field_behavior), set_size_field_behavior)
  949.     
  950.     def _fixed_field_lengths(self):
  951.         fixed_field_lengths = { }
  952.         for key in self._multivalued_fields:
  953.             length = self._get_size_field_length(key)
  954.             fixed_field_lengths[key] = {
  955.                 'size': length }
  956.         
  957.         return fixed_field_lengths
  958.  
  959.     _fixed_field_lengths = property(_fixed_field_lengths)
  960.     
  961.     def _get_size_field_length(self, key):
  962.         if self.size_field_behavior == 'apt-ftparchive':
  963.             return 16
  964.         if self.size_field_behavior == 'dak':
  965.             lengths = [ len(str(item['size'])) for item in self[key] ]
  966.             return max(lengths)
  967.  
  968.  
  969.  
  970. class Sources(Dsc, _PkgRelationMixin):
  971.     '''Represent an APT source package list'''
  972.     _relationship_fields = [
  973.         'build-depends',
  974.         'build-depends-indep',
  975.         'build-conflicts',
  976.         'build-conflicts-indep']
  977.     
  978.     def __init__(self, *args, **kwargs):
  979.         Dsc.__init__(self, *args, **kwargs)
  980.         _PkgRelationMixin.__init__(self, *args, **kwargs)
  981.  
  982.  
  983.  
  984. class Packages(Deb822, _PkgRelationMixin):
  985.     '''Represent an APT binary package list'''
  986.     _relationship_fields = [
  987.         'depends',
  988.         'pre-depends',
  989.         'recommends',
  990.         'suggests',
  991.         'breaks',
  992.         'conflicts',
  993.         'provides',
  994.         'replaces',
  995.         'enhances']
  996.     
  997.     def __init__(self, *args, **kwargs):
  998.         Deb822.__init__(self, *args, **kwargs)
  999.         _PkgRelationMixin.__init__(self, *args, **kwargs)
  1000.  
  1001.  
  1002.  
  1003. class _CaseInsensitiveString(str):
  1004.     '''Case insensitive string.
  1005.     '''
  1006.     
  1007.     def __new__(cls, str_):
  1008.         s = str.__new__(cls, str_)
  1009.         s.str_lower = str_.lower()
  1010.         s.str_lower_hash = hash(s.str_lower)
  1011.         return s
  1012.  
  1013.     
  1014.     def __hash__(self):
  1015.         return self.str_lower_hash
  1016.  
  1017.     
  1018.     def __eq__(self, other):
  1019.         return self.str_lower == other.lower()
  1020.  
  1021.     
  1022.     def lower(self):
  1023.         return self.str_lower
  1024.  
  1025.  
  1026. _strI = _CaseInsensitiveString
  1027.